#define BUFFER_SIZE 40

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>

typedef struct UartBuffer_t
{
	char data[BUFFER_SIZE];
	int write;
	int read;
}UartBuffer;

UartBuffer RxBuffer = { {} , 0, 0};
UartBuffer TxBuffer = { {} , 0, 0};	

int RxBufferGet(char *c)
{
		cli();
		//buffer empty?
		if (RxBuffer.read == RxBuffer.write) 
		{
			sei();
			return 0;
		}			
		
		*c = RxBuffer.data[RxBuffer.read];
		
		//set read
		RxBuffer.read++;
		if (RxBuffer.read >= BUFFER_SIZE) RxBuffer.read = 0;
		
		//USART_Transmit('G');
		//USART_putInt( abs(buffer.write - buffer.read) );
		//USART_Transmit('\n');
		
		sei();
		return 1;
}
int RxBufferAdd(char c)
{
		//buffer full?
		if(RxBuffer.write + 1 == RxBuffer.read || RxBuffer.read == 0 && RxBuffer.write + 1 == BUFFER_SIZE) return 0;
		
		//store data
		RxBuffer.data[RxBuffer.write] = c;
		
		//set write
		RxBuffer.write++;
		if (RxBuffer.write >= BUFFER_SIZE) RxBuffer.write = 0;
		
		return 1;
}
int TxBufferGet(char *c)
{
	//buffer empty?
	if (TxBuffer.read == TxBuffer.write) return 0;
	
	*c = TxBuffer.data[TxBuffer.read];
	
	//set read
	TxBuffer.read++;
	if (TxBuffer.read >= BUFFER_SIZE) TxBuffer.read = 0;
	
	return 1;
}
int TxBufferAdd(char c)
{
	cli();
	//buffer full?
	if(TxBuffer.write + 1 == TxBuffer.read || TxBuffer.read == 0 && TxBuffer.write + 1 == BUFFER_SIZE)
	{
		//UCSR0B |= (1<<UDRIE0);
		sei();
		return 0;
	}		 
	
	//
	UCSR0B |= (1<<UDRIE0);
	
	//store data
	TxBuffer.data[TxBuffer.write] = c;
	
	//set write
	TxBuffer.write++;
	if (TxBuffer.write >= BUFFER_SIZE) TxBuffer.write = 0;
	
	sei();
	return 1;
}

ISR(USART_UDRE_vect)
{
	char c;
	if (TxBufferGet(&c))
	{
		UDR0 = c;
	}
	else
	{
		//disable interrupt
		UCSR0B &= ~(1<<UDRIE0);
	}
}

ISR(USART_RX_vect)
{
	RxBufferAdd(UDR0);
}

void USART_Init( unsigned int ubrr)
{		
	/*Set baud rate */
	UBRR0H = (unsigned char)(ubrr>>8);
	UBRR0L = (unsigned char)ubrr;
	/*Enable receiver and transmitter */
	UCSR0B = (1<<UDRIE0)|(1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
	/* Set frame format: 8data, 1stop bit */
	UCSR0C = (0<<USBS0)|(3<<UCSZ00);
}

void USART_Transmit( unsigned char data ) 
{
	TxBufferAdd(data);
	//// Wait for empty transmit buffer */
	//while (!( UCSR0A & (1<< UDRE0)));
	//// Put data into buffer, sends the data */
	//UDR0 = data;
}

void USART_putInt(int number)
{
	char string[3];
	itoa(number, string, 16);
	
	TxBufferAdd('0');
	TxBufferAdd('x');
	
	if (strlen(string) != 2)
	{
		USART_Transmit('0');
	}
	
	for(int i = 0; string[i] != 0; i++) TxBufferAdd(string[i]);
	
	TxBufferAdd(' ');
}

int USART_Receive(char *c)
{
	return RxBufferGet(c);
}